#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

#include "debug.h"
#include "uart.h"

//#define SERIAL_PYH

static int serial_fd = -1;

/*****************************************************************************
 函 数 名 : eSerial_Init
 功能描述 : 串口初始化
 参     数 : char *name:设备名
            unsigned int baud:波特率
            int *piserial_fd:文件描述符指针，输出型参数
 返 回 值 : 
 调用函数 : 
 被调函数 : 
 
 修改历史 :
  0.日     期  : 2020年5月20日
    作     者 : thomas
    修改内容 : 新生成函数

*****************************************************************************/
teSerial_Status eSerial_Init( char *name, unsigned int baud, int *piserial_fd )
{
    int fd;
    static struct termios options;
    LOGD("Opening serial device '%s' at baud rate %ubps\n", name, baud);

    switch (baud)
    {
#ifdef B50

        case (50):
            baud = B50;
            break;
#endif /* B50 */
#ifdef B75

        case (75):
            baud = B75;
            break;
#endif /* B75 */
#ifdef B110

        case (110):
            baud = B110;
            break;
#endif /* B110 */
#ifdef B134

        case (134):
            baud = B134;
            break;
#endif /* B134 */
#ifdef B150

        case (150):
            baud = B150;
            break;
#endif /* B150 */
#ifdef B200

        case (200):
            baud = B200;
            break;
#endif /* B200 */
#ifdef B300

        case (300):
            baud = B300;
            break;
#endif /* B300 */
#ifdef B600

        case (600):
            baud = B600;
            break;
#endif /* B600 */
#ifdef B1200

        case (1200):
            baud = B1200;
            break;
#endif /* B1200 */
#ifdef B1800

        case (1800):
            baud = B1800;
            break;
#endif /* B1800 */
#ifdef B2400

        case (2400):
            baud = B2400;
            break;
#endif /* B2400 */
#ifdef B4800

        case (4800):
            baud = B4800;
            break;
#endif /* B4800 */
#ifdef B9600

        case (9600):
            baud = B9600;
            break;
#endif /* B9600 */
#ifdef B19200

        case (19200):
            baud = B19200;
            break;
#endif /* B19200 */
#ifdef B38400

        case (38400):
            baud = B38400;
            break;
#endif /* B38400 */
#ifdef B57600

        case (57600):
            baud = B57600;
            break;
#endif /* B57600 */
#ifdef B115200

        case (115200):
            baud = B115200;
            break;
#endif /* B115200 */
#ifdef B230400

        case (230400):
            baud = B230400;
            break;
#endif /* B230400 */
#ifdef B460800

        case (460800):
            baud = B460800;
            break;
#endif /* B460800 */
#ifdef B500000

        case (500000):
            baud = B500000;
            break;
#endif /* B500000 */
#ifdef B576000

        case (576000):
            baud = B576000;
            break;
#endif /* B576000 */
#ifdef B921600

        case (921600):
            baud = B921600;
            break;
#endif /* B921600 */
#ifdef B1000000

        case (1000000):
            baud = B1000000;
            break;
#endif /* B1000000 */
#ifdef B1152000

        case (1152000):
            baud = B1152000;
            break;
#endif /* B1152000 */
#ifdef B1500000

        case (1500000):
            baud = B1500000;
            break;
#endif /* B1500000 */
#ifdef B2000000

        case (2000000):
            baud = B2000000;
            break;
#endif /* B2000000 */
#ifdef B2500000

        case (2500000):
            baud = B2500000;
            break;
#endif /* B2500000 */
#ifdef B3000000

        case (3000000):
            baud = B3000000;
            break;
#endif /* B3000000 */
#ifdef B3500000

        case (3500000):
            baud = B3500000;
            break;
#endif /* B3500000 */
#ifdef B4000000

        case (4000000):
            baud = B4000000;
            break;
#endif /* B4000000 */

        default:
            LOGD("Unsupported baud rate speccified (%d)\n", baud);
            return E_SERIAL_ERROR;
    }

    fd = open(name, O_RDWR | O_NOCTTY);
    if ( fd < 0 )
    {
        LOGE("Couldn't open serial device \"%s\"(%s)\n", name, strerror(errno));
        return E_SERIAL_ERROR;
    }

    if ( tcgetattr(fd, &options) == -1 )
    {
        LOGE("Error getting port settings (%s)\n", strerror(errno));
        return E_SERIAL_ERROR;
    }

    options.c_iflag &= ~(INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IUCLC | IXON | IXANY | IXOFF);
    options.c_iflag = IGNBRK | IGNPAR;
    options.c_oflag &= ~(OPOST | OLCUC | ONLCR | OCRNL | ONOCR | ONLRET);
    options.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CRTSCTS);
    options.c_cflag |= CS8 | CREAD | HUPCL | CLOCAL;
    options.c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN);
    cfsetispeed(&options, baud);
    cfsetospeed(&options, baud);

    if ( tcsetattr(fd, TCSAFLUSH, &options) == -1 )
    {
        LOGE("Error setting port settings (%s)\n", strerror(errno));
        return E_SERIAL_ERROR;
    }

    *piserial_fd = serial_fd = fd;
    return E_SERIAL_OK;
}

/*****************************************************************************
 函 数 名 : eSerial_Read
 功能描述 : 读串口1字节数据到data中
 参     数 : unsigned char *data  
 返 回 值 : 
 调用函数 : 
 被调函数 : 
 
 修改历史 :
  0.日     期  : 2020年5月20日
    作     者 : thomas
    修改内容 : 新生成函数

*****************************************************************************/
teSerial_Status eSerial_Read( unsigned char *data )
{
    signed char res;

    res = read(serial_fd, data, 1);
    if ( res > 0 )
    {
//#ifdef SERIAL_PYH
#if 1
        printf("%02X ", *data);
#endif
    }
    else
    {
        if ( res == 0 )
        {
            //DEBUG_PRINTF( "Serial connection to module interrupted");
            //bRunning = 0;
        }

        return E_SERIAL_NODATA;
    }

    return E_SERIAL_OK;
}

/*****************************************************************************
 函 数 名 : eSerial_Write
 功能描述 : 写1字节的数据data到串口，写之前先判断串口是否忙
 参     数 : const unsigned char data  
 返 回 值 : 
 调用函数 : 
 被调函数 : 
 
 修改历史 :
  0.日     期  : 2020年5月20日
    作     者 : thomas
    修改内容 : 新生成函数

*****************************************************************************/
teSerial_Status eSerial_Write( const unsigned char data )
{
    int err, attempts = 0;
//#ifdef SERIAL_PYH
#if 1
    printf("%02X ", data);
#endif

    //TODO
    //while(isbusy()){usleep(1000)};  //读取gpio状态是否为busy

    err = write(serial_fd, &data, 1);
    if ( err < 0 )
    {
        if ( errno == EAGAIN )
        {
            for ( attempts = 0; attempts <= 5; attempts++ )
            {
                //while(isbusy()){usleep(1000)};  //读取gpio状态是否为busy
                err = write(serial_fd, &data, 1);
                if ( err < 0 )
                {
                    if ( (errno == EAGAIN) && (attempts == 5) )
                    {
                        LOGE("Error writing to module after %d attempts(%s)\n", attempts,
                             strerror(errno));
                        return E_SERIAL_ERROR;
                    }
                }
                else
                {
                    break;
                }
            }
        }
        else
        {
            LOGE("Error writing to module(%s)\n", strerror(errno));
            return E_SERIAL_ERROR;
        }
    }

    return E_SERIAL_OK;
}

/*****************************************************************************
 函 数 名 : eSerial_ReadBuffer
 功能描述 : 一次性读完所有数据
 参     数 : unsigned char *data  
            unsigned int *count  
 返 回 值 : 
 调用函数 : 
 被调函数 : 
 
 修改历史 :
  0.日     期  : 2020年5月20日
    作     者 : thomas
    修改内容 : 新生成函数

*****************************************************************************/
teSerial_Status eSerial_ReadBuffer( unsigned char *data, unsigned int *count )
{
    int res;

    res = read(serial_fd, data, *count);
    if ( res > 0 )
    {
        *count = res;
        return E_SERIAL_OK;
    }
    else
    {
        if ( res == 0 )
        {
            LOGD("Serial connection to module interrupted\n");
        }

        res = *count = 0;
        return E_SERIAL_NODATA;
    }
}

/*****************************************************************************
 函 数 名 : eSerial_WriteBuffer
 功能描述 : 写入指定长度数据
 参     数 : unsigned char *data  
            unsigned int count   
 返 回 值 : 
 调用函数 : 
 被调函数 : 
 
 修改历史 :
  0.日     期  : 2020年5月20日
    作     者 : thomas
    修改内容 : 新生成函数

*****************************************************************************/
teSerial_Status eSerial_WriteBuffer( unsigned char *data, unsigned int count )
{
    int attempts = 0;
    int total_sent_bytes = 0, sent_bytes = 0;

    while ( total_sent_bytes < count )
    {
        //while(isbusy()){usleep(1000)};  //读取gpio状态是否为busy
        sent_bytes = write(serial_fd, &data[total_sent_bytes], count - total_sent_bytes);
        if ( sent_bytes <= 0 )
        {
            if ( errno == EAGAIN )
            {
                if ( ++attempts >= 5 )
                {
                    LOGE("Error writing to module(%s)\n", strerror(errno));
                    return E_SERIAL_ERROR;
                }

                usleep(1000);
            }
            else
            {
                LOGE("Error writing to module(%s)\n", strerror(errno));
                return E_SERIAL_ERROR;
            }
        }
        else
        {
            attempts = 0;
            total_sent_bytes += sent_bytes;
        }
    }

    return E_SERIAL_OK;
}

teSerial_Status eSerial_Close( void )
{
    close(serial_fd);
    serial_fd = -1;
    return E_SERIAL_OK;
}